#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/time.h>
#include <stdint.h>

#include "icar.h"
#include "icar_msg.h"
#include "icar_log.h"
#include "icar_utils.h"
#include "icar_thread.h"
#include "icar_sock.h"

static void icar_client(void *);

pthread_mutex_t client_fdsets_mutex = PTHREAD_MUTEX_INITIALIZER;

void 
thread_msg(void *args)
{
	fdsets_t *fds = (fdsets_t *)args;

	int i = 0, m = 0, result;
	int maxfd;
	int sockfd;
	pthread_t tid;
	struct cliFds cliFds;

	struct timeval tv;
	tv.tv_sec = 0;
	tv.tv_usec = 100000; 

	while (1) {
		fds->rset = fds->allset;
		if (fds->index >= ARRAY_SIZE(fds->fdsets)) {
			m = 0;
			for (i = 0; i < ARRAY_SIZE(fds->fdsets); i++) {
				if (fds->fdsets[i] == -1)
					m++;
				else {
					maxfd = maxfd > fds->fdsets[i] ? maxfd : fds->fdsets[i];
				}
			}
					
			if (m == ARRAY_SIZE(fds->fdsets)) {
				LOCK_CLIENT_FDS();
				icar_fdset_list_delete(fds);
				UNLOCK_CLIENT_FDS();
				return;
			}
		}

		maxfd = fds->fdsets[0];
		for (i = 0; i < fds->index; i++) {
			if (fds->fdsets[i] > 0)
				maxfd = maxfd > fds->fdsets[i] ? maxfd : fds->fdsets[i];
		}

		result = select(maxfd + 1, &fds->rset, NULL, NULL, &tv);
		if (result < 0) {
			LOCK_CLIENT_FDS();
			for (i = 0; i < ARRAY_SIZE(fds->fdsets); i++) {
				fds->fdsets[i] = -1;
				close(fds->fdsets[i]);
			}
			icar_debug("Select error");
			icar_fdset_list_delete(fds);
			UNLOCK_CLIENT_FDS();
			return;
		} 
		if (result == 0)
			continue;

		for (i = 0; i < ARRAY_SIZE(fds->fdsets); i++) {
			if ((sockfd = fds->fdsets[i]) <= 0)
				continue;

			if (FD_ISSET(sockfd, &fds->rset)) {
				cliFds.index = i;
				cliFds.fds = &fds;
				result = pthread_create(&tid, NULL, (void *)icar_client, (void *)&cliFds);
				if (result != 0) {
					icar_debug(_("Failed to create a new thread client - then exiting"));
				}
				pthread_detach(tid);
			}
		}
	}
}

static void 
icar_client(void *args)
{
	struct cliFds *cliFds = (struct cliFds *)args;
	int index = cliFds->index;
	fdsets_t **fds = cliFds->fds;
	uint8_t buf[READ_BUF_LEN];
	uint8_t dest[READ_BUF_LEN] = { 0 };

	int nread;
	int count, n;

	if ((nread = read((*fds)->fdsets[index], buf, READ_BUF_LEN)) == 0) {
		LOCK_CLIENT_FDS();
		if ((*fds)->fdsets[index] > 0) {
			icar_daemon(LOG_DEBUG, "Client %d has been removed", (*fds)->fdsets[index]);
			shutdown((*fds)->fdsets[index], 2);
			close((*fds)->fdsets[index]);
			(*fds)->fdsets[index] = -1;
			FD_CLR((*fds)->fdsets[index], &(*fds)->allset);
			(*fds)->rset = (*fds)->allset;
		}
		UNLOCK_CLIENT_FDS();
		return;
	} else if (nread < 0) {
		icar_debug("Read data error");
		icar_daemon(LOG_DEBUG, "Read data error: %d[%s]", errno, strerror(errno));
		return;
	}

	icar_daemon(LOG_DEBUG, "Have read %d bytes", nread);


	count = 0;
	n = 0;
	while ((count = icar_readPkg(dest, READ_BUF_LEN, buf+n, nread-n)) > 0) {
		n += count;
		icar_daemon(LOG_DEBUG, "Read a whole package: %d", count);
		icar_process_request((*fds)->fdsets[index], dest, count);
		icar_daemon(LOG_DEBUG, "Handled one package of data");
	}
}
